package net.qiqbframework.modelling.command.fetching;

import net.qiqbframework.common.resolving.AnnotatedResolvedAttributes;
import net.qiqbframework.common.utils.AnnotationUtil;
import net.qiqbframework.common.utils.ReflectionUtil;
import net.qiqbframework.common.utils.StringUtils;
import net.qiqbframework.messaging.annotation.*;
import net.qiqbframework.modelling.command.Cmd;
import net.qiqbframework.modelling.command.voucher.AnnotatedLoadVoucherModel;
import net.qiqbframework.modelling.command.voucher.BizIdentifierVoucherModel;
import net.qiqbframework.modelling.command.voucher.FieldBizIdentifierVoucherModel;
import net.qiqbframework.modelling.command.voucher.LoadVoucherModel;
import net.qiqbframework.modelling.domain.Aggregate;
import net.qiqbframework.modelling.repository.AggregateBizIdentifierVoucher;
import net.qiqbframework.modelling.repository.AggregateRepository;

import java.lang.reflect.Field;
import java.util.*;

public class PreAnnotatedCommandModel<C, A> implements AggregateCommandModel<C, A> {

    private final Class<C> commandType;

    private Class<A> aggregateType;

    private FetchExecutableHandler fetchExecutableHandler = null;

    private String commandName;

    private String aggregateName;

    private final LoadVoucherModel<C> loadVoucherModel = new AnnotatedLoadVoucherModel<>();

    private final AggregateRepository<A> repository;

    private final HandlerParameterResolverFactory handlerParameterResolverFactory;
    private final MessageHandlingMemberFactory messageHandlingMemberFactory;

    public PreAnnotatedCommandModel(Class<C> commandType, AggregateRepository<A> repository, HandlerParameterResolverFactory handlerParameterResolverFactory, MessageHandlingMemberFactory messageHandlingMemberFactory) {
        this.commandType = commandType;
        this.repository = repository;
        this.handlerParameterResolverFactory = handlerParameterResolverFactory;
        this.messageHandlingMemberFactory = messageHandlingMemberFactory;
        this.commandName = commandType.getName();
        initialize();

    }

    private void initializeHandlers() {
        AnnotatedHandlerInspector<C> inspector = AnnotatedHandlerInspector.inspectType(commandType,
                handlerParameterResolverFactory,
                HandlerAttributesPostProcessor.EMPTY,
                messageHandlingMemberFactory);
        // 汇总多个执行器
        for (Map.Entry<Class<?>, SortedSet<MessageExecutableHandler>> handlersPerType : inspector.getAllHandlers().entrySet()) {

            for (MessageExecutableHandler handler : handlersPerType.getValue()) {
                handler.unwrap(FetchExecutableHandler.class).ifPresent(cmh -> {
                    fetchExecutableHandler = cmh;
                });
            }
        }
    }

    private void initialize() {
        // 获取member

        final Optional<Map<String, Object>> annotationAttributes = AnnotationUtil.findAnnotationAttributes(commandType, Cmd.class);
        if (annotationAttributes.isPresent()) {

            this.aggregateType = (Class<A>) annotationAttributes.get().get("value");
            initializeHandlers();
        }

        for (Field f : ReflectionUtil.fieldsOf(commandType)) {
            final AnnotatedResolvedAttributes annotationAttribute = new AnnotatedResolvedAttributes(f);
            if (annotationAttribute.get("BizIdentifierVoucher.value") != null) {
                String routingBizName = null;
                if (!StringUtils.isEmptyOrNull(annotationAttribute.get("BizIdentifierVoucher.value"))) {
                    routingBizName = annotationAttribute.get("BizIdentifierVoucher.value").toString();
                }
                if (!StringUtils.isEmptyOrNull(annotationAttribute.get("BizIdentifierVoucher.routingNaming"))) {
                    routingBizName = annotationAttribute.get("BizIdentifierVoucher.routingNaming").toString();
                }
                if (routingBizName == null) {
                    routingBizName = f.getName();
                }
                ReflectionUtil.ensureAccessible(f);
                loadVoucherModel.addVoucherModel(new FieldBizIdentifierVoucherModel<>(f, routingBizName));
            }

        }

    }

    @Override
    public Class<C> commandType() {
        return commandType;
    }

    @Override
    public String commandName() {
        return commandName;
    }

    @Override
    public String aggregateName() {
        return aggregateName;
    }

    @Override
    public Class<A> aggregateRootType() {
        return aggregateType;
    }

    @Override
    public Aggregate<A> obtainAggregate(C commandObject) {
        if (fetchExecutableHandler != null) {

            try {

                return repository.newInstance(() -> (A) fetchExecutableHandler.handle(GenericFetchMessage.of(commandObject), commandObject));
            } catch (Exception e) {
                if (e instanceof RuntimeException runtimeException){
                    throw runtimeException;
                }
                throw new RuntimeException(e);
            }
        }
        // 根据加载凭证加载
        final List<AggregateBizIdentifierVoucher> bizIdentifierVouchers = new ArrayList<>();
        for (BizIdentifierVoucherModel<?, C> bizIdentifierVoucherModel : loadVoucherModel.bizIdentifierVoucherModels()) {
            final Optional<?> identifierVoucher = bizIdentifierVoucherModel.bizIdentifierVoucher(commandObject);
            if (identifierVoucher.isPresent()) {
                // 根据这个来加载
                final AggregateBizIdentifierVoucher bizIdentifierVoucher = new AggregateBizIdentifierVoucher(bizIdentifierVoucherModel.routingBizIdentifierName(), identifierVoucher.get());

                bizIdentifierVouchers.add(bizIdentifierVoucher);
                final Map<AggregateBizIdentifierVoucher, Aggregate<A>> bizIdentifierVoucherAggregateMap = repository.batchLoad(bizIdentifierVouchers);
                return bizIdentifierVoucherAggregateMap.get(bizIdentifierVoucher);
            }
        }
        return null;
    }


}
